﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace MvcShop.Models.Repositories
{
    public class TrueCriteria : BaseCriteria
    {
        public override string Expression
        {
            get { return "(1=1)"; }
        }
    }

    public class EqualParameterCriteria : BaseCriteria
    {
        CriteriaParameter cp;
        public EqualParameterCriteria(String field, Object value)
        {
            this.CriteriaType = CriteriaType.Parameter;
            cp = new CriteriaParameter(ParameterNameFormat(field), value, field);
            parameters.Add(cp);
        }

        public override string Expression
        {
            get { return string.Format("{2}{0}={3}{1}", cp.Field, cp.Name, FieldPrefix, NamePrefix); }
        }
    }

    public class EqualCriteria : BaseCriteria
    {
        protected String field;

        protected Object value;

        public EqualCriteria(String field, Object value)
        {

            this.field = field;

            this.value = value;

        }

        public override string Expression
        {
            get
            {
                if (value.GetType() == typeof(int) ||
                    value.GetType() == typeof(double) ||
                    value.GetType() == typeof(bool))
                    return string.Format("{2}{0}={1}", field, value, FieldPrefix);
                return string.Format("{2}{0}='{1}'", field, value, FieldPrefix);
            }
        }

    }

    public class NotEqualParameterCriteria : BaseCriteria
    {
        CriteriaParameter cp;
        public NotEqualParameterCriteria(String field, Object value)
        {
            this.CriteriaType = CriteriaType.Parameter;
            cp = new CriteriaParameter(ParameterNameFormat(field), value, field);
            parameters.Add(cp);
        }

        public override string Expression
        {
            get
            {
                return string.Format("{2}{0}!={3}{1}", cp.Field, cp.Name, FieldPrefix, NamePrefix);
            }
        }
    }

    public class NotEqualCriteria : BaseCriteria
    {
        protected String field;

        protected Object value;

        public NotEqualCriteria(String field, Object value)
        {

            this.field = field;

            this.value = value;


        }
        #region ICriteria 成员

        public override string Expression
        {
            get
            {
                if (value.GetType() == typeof(int) ||
                    value.GetType() == typeof(double) ||
                    value.GetType() == typeof(bool))
                    return string.Format("{2}{0}!={1}", field, value, FieldPrefix);
                return string.Format("{2}{0}!='{1}'", field, value, FieldPrefix);

            }
        }

        #endregion
    }

    public class GreaterThanParameterCriteria : BaseCriteria
    {
        CriteriaParameter cp;
        public GreaterThanParameterCriteria(String field, Object value)
        {
            this.CriteriaType = CriteriaType.Parameter;
            cp = new CriteriaParameter(ParameterNameFormat(field), value, field);
            parameters.Add(cp);
        }

        public override string Expression
        {
            get
            {
                return string.Format("{2}{0}>{3}{1}", cp.Field, cp.Name, FieldPrefix, NamePrefix);
            }
        }
    }

    public class GreaterThanCriteria : BaseCriteria
    {

        protected String field;

        protected Object value;

        public GreaterThanCriteria(String field, Object value)
        {

            this.field = field;

            this.value = value;

        }

        #region ICriteria 成员

        public override string Expression
        {
            get
            {
                if (value.GetType() == typeof(int) ||
                    value.GetType() == typeof(double))
                    return string.Format("{2}{0}>{1}", field, value, FieldPrefix);
                return string.Format("{2}{0}>'{1}'", field, value, FieldPrefix);

            }
        }

        #endregion
    }

    public class GreaterEqualThanParameterCriteria : BaseCriteria
    {
        CriteriaParameter cp;
        public GreaterEqualThanParameterCriteria(String field, Object value)
        {
            this.CriteriaType = CriteriaType.Parameter;
            cp = new CriteriaParameter(ParameterNameFormat(field), value, field);
            parameters.Add(cp);
        }

        public override string Expression
        {
            get
            {
                return string.Format("{2}{0}>={3}{1}", cp.Field, cp.Name, FieldPrefix, NamePrefix);
            }
        }
    }

    public class GreaterEqualThanCriteria : BaseCriteria
    {

        protected String field;

        protected Object value;

        public GreaterEqualThanCriteria(String field, Object value)
        {

            this.field = field;

            this.value = value;

        }

        #region ICriteria 成员

        public override string Expression
        {
            get
            {
                if (value.GetType() == typeof(int) ||
                    value.GetType() == typeof(double))
                    return string.Format("{2}{0}>={1}", field, value, FieldPrefix);
                return string.Format("{2}{0}>='{1}'", field, value, FieldPrefix);
            }
        }

        #endregion
    }

    public class LessThanParameterCriteria : BaseCriteria
    {
        CriteriaParameter cp;
        public LessThanParameterCriteria(String field, Object value)
        {
            this.CriteriaType = CriteriaType.Parameter;
            cp = new CriteriaParameter(ParameterNameFormat(field), value, field);
            parameters.Add(cp);
        }

        public override string Expression
        {
            get
            {
                return string.Format("{2}{0}<{3}{1}", cp.Field, cp.Name, FieldPrefix, NamePrefix);
            }
        }
    }

    public class LessThanCriteria : BaseCriteria
    {

        protected String field;

        protected Object value;

        public LessThanCriteria(String field, Object value)
        {

            this.field = field;

            this.value = value;

        }

        #region ICriteria 成员

        public override string Expression
        {
            get
            {
                if (value.GetType() == typeof(int) ||
                    value.GetType() == typeof(double))
                    return string.Format("{2}{0}<{1}", field, value, FieldPrefix);
                return string.Format("{2}{0}<'{1}'", field, value, FieldPrefix);
            }
        }

        #endregion
    }

    public class LessEqualThanParameterCriteria : BaseCriteria
    {
        CriteriaParameter cp;
        public LessEqualThanParameterCriteria(String field, Object value)
        {
            this.CriteriaType = CriteriaType.Parameter;
            cp = new CriteriaParameter(ParameterNameFormat(field), value, field);
            parameters.Add(cp);
        }

        public override string Expression
        {
            get
            {
                return string.Format("{2}{0}<={3}{1}", cp.Field, cp.Name, FieldPrefix, NamePrefix);
            }
        }
    }

    public class LessEqualThanCriteria : BaseCriteria
    {

        protected String field;

        protected Object value;

        public LessEqualThanCriteria(String field, Object value)
        {

            this.field = field;

            this.value = value;

        }

        #region ICriteria 成员

        public override string Expression
        {
            get
            {
                if (value.GetType() == typeof(int) ||
                    value.GetType() == typeof(double))
                    return string.Format("{2}{0}<={1}", field, value, FieldPrefix);
                return string.Format("{2}{0}<='{1}'", field, value, FieldPrefix);
            }
        }

        #endregion
    }

    public class LikeCriteria : BaseCriteria
    {
        public enum LikeCriteriaType
        {
            NoneWildcard,
            LeftWildcard,
            RightWildcard,
            BothWildcard
        }

        LikeCriteriaType wildcard = LikeCriteriaType.NoneWildcard;

        protected String field;

        protected Object value;


        public LikeCriteria(String field, Object value, LikeCriteriaType wildcard)
        {

            this.field = field;

            this.value = value;

            this.wildcard = wildcard;
        }

        public LikeCriteria(String field, Object value) : this(field, value, LikeCriteriaType.BothWildcard) { }

        #region ICriteria 成员

        public override string Expression
        {
            get
            {
                switch (wildcard)
                {
                    case LikeCriteriaType.NoneWildcard:
                        return string.Format("{2}{0} like '{1}'", field, value, FieldPrefix);
                    case LikeCriteriaType.LeftWildcard:
                        return string.Format("{2}{0} like '%{1}'", field, value, FieldPrefix);
                    case LikeCriteriaType.RightWildcard:
                        return string.Format("{2}{0} like '{1}%'", field, value, FieldPrefix);
                    case LikeCriteriaType.BothWildcard:
                        return string.Format("{2}{0} like '%{1}%'", field, value, FieldPrefix);
                    default:
                        return string.Empty;
                }
            }
        }

        #endregion
    }

    public class BetweenCriteria : BaseCriteria
    {
        string begin_expression;
        string end_expression;
        string field;


        public BetweenCriteria(String field, string begin_expression, string end_expression)
        {
            this.field = field;
            this.begin_expression = begin_expression;
            this.end_expression = end_expression;
        }

        #region ICriteria 成员

        public override string Expression
        {
            get
            {
                return string.Format("{3}{0} BETWEEN {1} AND {2}", field, begin_expression, end_expression, FieldPrefix);
            }
        }

        #endregion

    }

    public class BetweenParameterCriteria : BaseCriteria
    {
        CriteriaParameter cp1;
        CriteriaParameter cp2;

        public BetweenParameterCriteria(string field, object begin_expression, object end_expression)
        {
            string fieldPamaName1 = string.Format("{0}1", field);
            string fieldPamaName2 = string.Format("{0}2", field);


            cp1 = new CriteriaParameter(ParameterNameFormat(fieldPamaName1), begin_expression, field);
            parameters.Add(cp1);
            cp2 = new CriteriaParameter(ParameterNameFormat(fieldPamaName2), end_expression, field);
            parameters.Add(cp2);
            this.CriteriaType = CriteriaType.Parameter;

        }

        public override string Expression
        {
            get
            {
                return string.Format("{3}{0} BETWEEN {4}{1} AND {4}{2}", cp1.Field, cp1.Name, cp2.Name, FieldPrefix, NamePrefix);
            }
        }
    }

    public class NotCriteria : BaseCriteria
    {

        protected ICriteria c1;

        public NotCriteria(ICriteria c1)
        {

            this.c1 = c1;
            if (c1.CriteriaType == CriteriaType.Parameter)
            {
                parameters.AddRange(c1.Parameters);
                CriteriaType = CriteriaType.Parameter;
            }
        }

        #region ICriteria 成员

        public override string Expression
        {
            get { return string.Format("NOT({0})", this.c1.Expression); }
        }

        #endregion
    }

    public class AndCriteria : BaseCriteria
    {

        protected ICriteria c1, c2;

        public AndCriteria(ICriteria c1, ICriteria c2)
        {
            this.c1 = c1;
            this.c2 = c2;
            CriteriaType = c1.CriteriaType | c2.CriteriaType;
            c1.UniqueCriteriaParameter(c2);
            if (c1.CriteriaType == CriteriaType.Parameter)
                parameters.AddRange(c1.Parameters);
            if (c2.CriteriaType == CriteriaType.Parameter)
                parameters.AddRange(c2.Parameters);
        }

        #region ICriteria 成员

        public override string Expression
        {
            get { return string.Format("{0} AND {1}", c1.Expression, c2.Expression); }
        }

        #endregion

    }

    public class BracketsAndCriteria : BaseCriteria
    {

        protected ICriteria c1, c2;

        public BracketsAndCriteria(ICriteria c1, ICriteria c2)
        {
            this.c1 = c1;
            this.c2 = c2;
            CriteriaType = c1.CriteriaType | c2.CriteriaType;
            c1.UniqueCriteriaParameter(c2);
            if (c1.CriteriaType == CriteriaType.Parameter)
                parameters.AddRange(c1.Parameters);
            if (c2.CriteriaType == CriteriaType.Parameter)
                parameters.AddRange(c2.Parameters);
        }

        #region ICriteria 成员

        public override string Expression
        {
            get { return string.Format("({0}) AND ({1})", c1.Expression, c2.Expression); }
        }

        #endregion

    }

    public class OrCriteria : BaseCriteria
    {

        protected ICriteria c1, c2;

        public OrCriteria(ICriteria c1, ICriteria c2)
        {
            this.c1 = c1;
            this.c2 = c2;
            CriteriaType = c1.CriteriaType | c2.CriteriaType;
            c1.UniqueCriteriaParameter(c2);
            if (c1.CriteriaType == CriteriaType.Parameter)
                parameters.AddRange(c1.Parameters);
            if (c2.CriteriaType == CriteriaType.Parameter)
                parameters.AddRange(c2.Parameters);
        }

        #region ICriteria 成员

        public override string Expression
        {
            get { return string.Format("{0} OR {1}", c1.Expression, c2.Expression); }
        }

        #endregion

    }

    public class BracketsOrCriteria : BaseCriteria
    {

        protected ICriteria c1, c2;

        public BracketsOrCriteria(ICriteria c1, ICriteria c2)
        {
            this.c1 = c1;
            this.c2 = c2;
            CriteriaType = c1.CriteriaType | c2.CriteriaType;
            c1.UniqueCriteriaParameter(c2);
            if (c1.CriteriaType == CriteriaType.Parameter)
                parameters.AddRange(c1.Parameters);
            if (c2.CriteriaType == CriteriaType.Parameter)
                parameters.AddRange(c2.Parameters);
        }

        #region ICriteria 成员

        public override string Expression
        {
            get { return string.Format("({0}) OR ({1})", c1.Expression, c2.Expression); }
        }

        #endregion

    }

    public static class CriteriaExtentsions
    {
        static public void UniqueCriteriaParameter(this ICriteria c1, ICriteria c2)
        {
            bool isBothParameter = (c1.CriteriaType & c2.CriteriaType) > 0;
            if (isBothParameter)
            {
                foreach (var p in c1.Parameters)
                {
                    var it = c2.Parameters.FirstOrDefault(x => x.Name == p.Name);
                    if (it != null)
                    {
                        int index = 1;
                        string testName = it.Name + index.ToString();
                        while (c2.Parameters.ToList().Exists(x => x.Name == testName) ||
                            c1.Parameters.ToList().Exists(x => x.Name == testName))
                        {
                            ++index;
                            testName = it.Name + index.ToString();
                        }
                        it.Name = testName;
                    }
                }
            }
        }
    }

    /// <summary>
    /// "IN" 查询语法
    /// </summary>  
    public class ContainsCriteria
    {
        /// <summary>
        /// 让 EF 支持类似 SQL "IN" 的语法   ContainsExpression
        /// </summary>  
        public static Expression<Func<TElement, bool>> ContainsExpression<TElement, TValue>(Expression<Func<TElement, TValue>> valueSelector, IEnumerable<TValue> values)
        {
            if (null == valueSelector)
            {
                throw new ArgumentNullException("valueSelector");
            }

            if (null == values)
            {
                throw new ArgumentNullException("values");
            }

            ParameterExpression p = valueSelector.Parameters.Single();

            if (!values.Any())
            {
                return e => false;
            }

            var equals = values.Select(value => (Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue))));
            var body = equals.Aggregate<Expression>((accumulate, equal) => Expression.Or(accumulate, equal));
            return Expression.Lambda<Func<TElement, bool>>(body, p);
        }
    }

    
}
